
// Copyright (c) 2009 Mohannad Alharthi (mohannad.harthi@gmail.com)
// All rights reserved.
// This source code is licensed under the BSD license, which can be found in
// the LICENSE.txt file.

#ifdef THREE_ADD_INTERMEDIATE

#include <sstream>

#include "misc.h"
#include "intermediate.h"

// VariableOperand class implementation

std::string VariableOperand::GetAsmOperand(const CodeGenerator& code_gen) {
  std::stringstream operand_stream;
  const VariableSymbol* variable_symbol = GetSymbol();

  operand_stream << (variable_symbol->data_type() == kIntegerType? "dword " : "byte ");
  if (variable_symbol->kind() == kLocal)
    operand_stream << "ptr [ebp - "
                   << (variable_symbol->offset() + variable_symbol->size())
                   << "]";
  else
    operand_stream << "ptr [ebp - " << (variable_symbol->offset() + 8) << "]";

  return operand_stream.str();
}

// ArrayOperand class inplementation

std::string ArrayOperand::GetAsmOperand(const CodeGenerator& code_gen) {
  std::stringstream operand_stream;
  const VariableSymbol* array_symbol = GetSymbol();

  //code_gen.LoadOperandToReg("esi", index_operand_);
  operand_stream << (array_symbol->data_type() == kIntegerType? "dword " : "byte ")
                 << "ptr [ebp + esi * "
                 << array_symbol->element_size()
                 << " - " << (array_symbol->offset() + array_symbol->size())
                 << "]";

  return operand_stream.str();
}

std::string ArrayOperand::GetIntermediateOperand() {
  return utilities::Format("%S[%S]", data(),
    index_operand_->GetIntermediateOperand());
}

// IntermediateInstr class implemetation

std::string IntermediateInstr::GetAsString() { 
  switch (operation_) {
  case Enter:
    return "enter " + operand1_->GetIntermediateOperand();
  case Param:
    return "param " + operand1_->GetIntermediateOperand();
  case Call:
    // return operand1_->GetIntermediateOperand(); + " = call " +
    //   operand2_->GetIntermediateOperand();
    return utilities::Format("%S = call %S", operand1_->GetIntermediateOperand(),
      operand2_->GetIntermediateOperand());
  case Return:
    {
      std::string ret = "return ";
      if (operand1_ != NULL)
        ret += operand1_->GetIntermediateOperand();;
      return ret;
    }
  case PrintInt:
    return "printInt " + operand1_->GetIntermediateOperand();
  case PrintStr:
    return "printStr " + operand1_->GetIntermediateOperand();
  case PrintChar:
    return "printChar " + operand1_->GetIntermediateOperand();
  case ReadInt:
    return "readInt " + operand1_->GetIntermediateOperand();
  case ReadStr:
    return "readStr " + operand1_->GetIntermediateOperand();
  case IncStackPtr:
    return "incStackPtr " + operand1_->GetIntermediateOperand();
  case DecStackPtr:
    return "decStackPtr " + operand1_->GetIntermediateOperand();
  case If:
    // Conditional jump
    return "if " + operand1_->GetIntermediateOperand() +
           " goto " + operand2_->GetIntermediateOperand();
  case Goto:
    // Unconditional jump
    return "goto " + operand1_->GetIntermediateOperand();
  case Equal:
    // Copy instruction (assignment)
    return operand1_->GetIntermediateOperand(); + " = " +
      operand2_->GetIntermediateOperand();
  default:
    std::string operation_str;
    switch (operation_) {
    case And:
      operation_str = "&";
      break;
    case Or:
      operation_str = "|";
      break;
    case EqualEqual:
      operation_str = "==";
      break;
    case NotEq:
      operation_str = "!=";
      break;
    case LessOrEq:
      operation_str = "<=";
      break;
    case GreaterOrEq:
      operation_str = ">=";
      break;
    // Otherwise: one-char tokens, the token code is equal to its ASCII code
    default:
      operation_str = static_cast<char>(operation_);
      break;
    }
    // Arithmatic or logical operations
    if ((operation_ == kMinus || operation_ == Not) && operand3_ == NULL)
      // Unary operation
      return utilities::Format("%S = %S%S", operand1_->GetIntermediateOperand(),
        operation_str, operand2_->GetIntermediateOperand());
    else
      // Binary operation
      return utilities::Format("%S = %S %S %S", operand1_->GetIntermediateOperand(),
        operand2_->GetIntermediateOperand(), operation_str,
        operand3_->GetIntermediateOperand());
  }
}

#endif // THREE_ADD_INTERMEDIATE
